import { RegistModel, UserTypeX } from './../entity/TT';
import { NGXLogger } from 'ngx-logger';
import { MatDialog } from '@angular/material/dialog';
import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http";

import { Observable, of } from "rxjs";
import { catchError, map, tap } from "rxjs/operators";

import { environment } from "src/environments/environment";

import { Reply } from "../entity/Reply";
import { User, BookedWrapper, Booked, Settings } from "../entity/TT";
import { Avatar } from "../entity/Avatar";
import { Calendar, Params } from "../entity/Calendar";

const httpOptions = {
  headers: new HttpHeaders({
    "Content-Type": "application/json",
  }),
  params: null,
  withCredentials: true,
};

@Injectable({
  providedIn: "root",
})
export class RequestReplyService {
  private meUrl;
  private passwordUrl;

  private getRtcUrl;

  private getSetUrl;
  private saveSetUrl;

  private getUserUrl;
  private setUserTypeUrl;

  private getConferenceUrl;
  private listConferenceUrl;
  private saveConferenceUrl;
  private giveCoinUrl;
  private giveFabulousUrl;
  private bookedCurrentUrl;
  private bookedExitUrl;

  private recordListBookedUrl;
  private recordListMessageUrl;

  private calendarUrl;

  private loginUrl;
  private logoutUrl;
  private registerUrl;

  showMaterialUrl;

  me: User;
  avatar: Avatar;
  conferenceId: string;

  constructor(
    private log: NGXLogger,
    private http: HttpClient,
    private dialog: MatDialog
    ) {
    this.log.debug("environment", environment);
    this.init(environment);
  }

  init(env) {
    this.log.debug("init", env);
    const apiUrl = env.apiUrl+'/classroom/v1';

    this.meUrl = apiUrl + "/me/get/info";
    this.passwordUrl = apiUrl + "/me/get/accessToken";
    this.getRtcUrl = apiUrl + "/init/get/rtc";

    this.getSetUrl = apiUrl + "/settings/load";
    this.saveSetUrl = apiUrl + "/settings/save";

    this.getUserUrl = apiUrl + "/user/get";
    this.setUserTypeUrl = apiUrl + "/user/setUserType";

    this.getConferenceUrl = apiUrl + "/conference/current";
    this.listConferenceUrl = apiUrl + "/conference/list";
    this.saveConferenceUrl = apiUrl + "/conference/save";

    this.giveCoinUrl = apiUrl + "/gift/give/coin";
    this.giveFabulousUrl = apiUrl + "/gift/give/fabulous";
    this.bookedCurrentUrl = apiUrl + "/booked/current";
    this.bookedExitUrl = apiUrl + "/booked/exit";

    this.recordListMessageUrl = apiUrl + "/record/list/message";

    this.calendarUrl = apiUrl + "/calendar/load";
    this.recordListBookedUrl = apiUrl + "/calendar/list/booked";

    this.showMaterialUrl = apiUrl + "/material/preview";
    const passportUrl = env.apiUrl+'/ua/passport';
    this.loginUrl = passportUrl + "/login";
    this.logoutUrl = passportUrl + "/logout";
    this.registerUrl = passportUrl + "/register";

  }

  GetBaseUrl() {
    return environment.baseUrl;
  }

  Logout() {
    return this.GetReply(this.logoutUrl);
  }

  Register(registerDTO: RegistModel): Observable<Reply> {
    return this.PostReply(this.registerUrl, null, registerDTO);
  }

  GetAccessToken(param: { loginId: string; password: string }) {
    return this.PostReply(this.loginUrl, null, param);
  }

  GetRtc(): Observable<any> {
    return this.GetReply(this.getRtcUrl).pipe(
      map((reply) => {
        if (reply && reply.success) {
          return reply.data;
        } else {
          this.log.error("error", reply);
        }
      })
    );
  }

  GetSetting(): Observable<Settings> {
    return this.GetReply(this.getSetUrl).pipe(
      map((reply) => {
        if (reply && reply.success) {
          return reply.data;
        } else {
          this.log.debug("error", reply);
        }
      })
    );
  }

  SetUserType(ut: UserTypeX) {
    return this.PostReply(this.setUserTypeUrl, null, ut.name);
  }

  SaveSetting(set: Settings): Observable<Reply> {
    return this.PostReply(this.saveSetUrl, null, set);
  }

  GetCalendar(params: Params): Observable<Calendar> {
    return this.GetReply(this.calendarUrl, params).pipe(
      map((reply) => {
        if (reply && reply.success) {
          return reply.data;
        } else {
          this.log.debug("error", reply);
        }
      })
    );
  }

  ExitBooked(booked: Booked) {
    return this.GetReply(this.bookedExitUrl, { sn: booked.sn }).pipe(
      map((reply) => {
        if (reply && reply.success) {
          return reply.data;
        }
      })
    );
  }

  GetCurrentBookedList(): Observable<BookedWrapper> {
    return this.GetReply(this.bookedCurrentUrl).pipe(
      map((reply) => {
        if (reply && reply.success) {
          const bookeds = reply.data;

          const w: BookedWrapper = {
            current: bookeds[0] || null,
            next: bookeds[1] || null,
            delay: reply.extra.delay || null,
          };
          return w;
        }
      })
    );
  }

  GetBookedList(params: any): Observable<any> {
    return this.GetReply(this.recordListBookedUrl, params).pipe(
      map((reply) => {
        if (reply && reply.success) {
          const bookeds = reply.data;
          return bookeds;
        }
      })
    );
  }

  GetBookedMessage(booked: Booked): Observable<any[]> {
    return this.GetReply(this.recordListMessageUrl, { sn: booked.sn }).pipe(
      map((reply) => {
        if (reply && reply.success) {
          return reply.data;
        }
      })
    );
  }

  GiveCoin(participants: string[], amount: number): Observable<boolean> {
    return this.GetReply(this.giveCoinUrl, {
      participants,
      amount,
      conferenceId: this.conferenceId,
    }).pipe(
      map((reply) => {
        if (reply && reply.success) {
          const data = reply.data;
          return data;
        }
      })
    );
  }

  GiveFabulous(participants: string[], amount: number): Observable<boolean> {
    return this.GetReply(this.giveFabulousUrl, {
      participants,
      amount,
      conferenceId: this.conferenceId,
    }).pipe(
      map((reply) => {
        if (reply && reply.success) {
          const data = reply.data;
          return data;
        }
      })
    );
  }

  GetPassword(): Observable<string> {
    return this.GetReply(this.passwordUrl).pipe(
      map((reply) => {
        if (reply && reply.success) {
          const data = reply.data;
          return data;
        }
      })
    );
  }

  LoadMe(): User {
    return this.me;
  }

  GetMe(): Observable<User> {
    return this.GetReply(this.meUrl).pipe(
      map((reply) => {
        if (reply && reply.success) {
          const data = reply.data;
          const str = JSON.stringify(data);
          this.me = JSON.parse(str);
          this.avatar = Avatar.from(this.me);
          return this.me;
        }
        // else{
        //   const dialogRef = this.dialog.open(MessageDialogComponent, {
        //     width: '250px',
        //     data:{message: reply.msg}
        //   });
        // }
      })
    );
  }

  GetMeFromParticipant(participant: string) {
    const me = this.LoadMe();
    if (me.participant === participant) {
      return me;
    }
    return null;
  }

  GetUser(uin: string): Observable<User> {
    this.log.debug("GetUser:", uin);
    return this.GetReply(this.getUserUrl, { uin }).pipe(
      map((reply) => {
        if (reply && reply.success) {
          const data = reply.data;
          const str = JSON.stringify(data);
          const u: User = JSON.parse(str);
          return u;
        }
      })
    );
  }

  GetConference(): Observable<string> {
    return this.GetReply(this.getConferenceUrl).pipe(
      map((reply) => {
        if (reply && reply.success) {
          this.conferenceId = reply.data;
          return this.conferenceId;
        }
      })
    );
  }

  ListConference(): Observable<Booked[]> {
    return this.GetReply(this.listConferenceUrl).pipe(
      map((reply) => {
        if (reply && reply.success) {
          return reply.data;
        }
      })
    );
  }

  SaveConference(sn: string): Observable<Reply> {
    return this.PostReply(this.saveConferenceUrl, null, sn);
/**
 *
 .pipe(
   map((reply) => {
     if (reply && reply.success) {
       return reply.data;
      }
    })
    );
    */
  }

  GetReply(url: string, params?: object): Observable<Reply> {
    this.log.debug("GetReply:", url, params);
    if (params) {
      let hps = new HttpParams();
      for (const k in params) {
        hps = hps.set(k, params[k]);
      }
      httpOptions.params = hps;
    }

    this.log.debug("GetReply", url);

    return this.http.get<Reply>(url, httpOptions).pipe(
      tap((_) => this.logx("fetched Reply:", _)),
      catchError(this.handleError<Reply>("GetReply", null))
    );
  }

  PostReply(url: string, params?: any, body?: any): Observable<Reply> {
    this.log.debug("PostReply:", url, params, body);
    if (params) {
      let hps = new HttpParams();
      for (const k in params) {
        hps = hps.set(k, params[k]);
      }
      httpOptions.params = hps;
    }

    return this.http.post<Reply>(url, body, httpOptions)
      .pipe(
        tap(_ => this.log.info('fetched Reply:', _)),
        // catchError( _ => this.handleError<Reply>('PostReply', _))
      );
  }

  private handleError<T>(operation = "operation", result?: T) {
    return (error: any): Observable<T> => {
      // TODO: send the error to remote logging infrastructure
      this.log.error(`${operation} handleError:`, error);

      console.dir(error.headers.get("location")); // log to console instead

      // log to console instead

      // TODO: better job of transforming error for user consumption
      // this.log(`${operation} failed: ${error.message}`);

      // Let the app keep running by returning an empty result.
      return of(result as T);
    };
  }

  private logx(message: string, ...obj: any[]) {
    this.log.debug("msg:", message, obj);
  }
}
